package com.holly.unit.gen.starter.engine;

import cn.hutool.core.util.StrUtil;
import com.holly.unit.gen.api.config.GenCodeCfg;
import com.holly.unit.gen.api.config.GenFileCfg;
import com.holly.unit.gen.api.constants.GenFileCfgConstants;
import com.holly.unit.gen.api.util.FileUtil;
import com.holly.unit.gen.api.util.FreemarkerFormatUtil;
import com.holly.unit.gen.starter.config.builder.ConfigBuilder;
import com.holly.unit.gen.starter.util.GenFileUtil;
import freemarker.cache.FileTemplateLoader;
import freemarker.cache.MultiTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

import java.io.*;
import java.util.List;
import java.util.Map;

/**
 * 类描述: Freemarker 模板引擎实现文件输出
 *
 * @author fxr
 * @version 1.0.0
 * @date 2022/1/19 16:26
 */
public class FreemarkerTemplateEngine extends AbstractTemplateEngine{

    private Configuration configuration;


    @Override
    public FreemarkerTemplateEngine init(List<File> templateRootDirs, String encode) {
        try{
            configuration = new Configuration(Configuration.VERSION_2_3_28);
            FileTemplateLoader[] loaders = new FileTemplateLoader[templateRootDirs.size()];

            for (int i = 0; i < templateRootDirs.size(); i++) {
                File file = templateRootDirs.get(i);
                loaders[i] = new FileTemplateLoader(file);
            }

            MultiTemplateLoader multiTemplateLoader = new MultiTemplateLoader(loaders);
            configuration.setTemplateLoader(multiTemplateLoader);
            configuration.setNumberFormat("###############");
            configuration.setBooleanFormat("true,false");
            configuration.setDefaultEncoding(encode);
        }catch (Exception e) {
            e.printStackTrace();
            return null;
        }
        return this;
    }

    @Override
    public FreemarkerTemplateEngine batchOutput(ConfigBuilder configBuilder) {

        try{
            this.genFileAbsolutePath(configBuilder);
        }catch (Exception e) {
            e.printStackTrace();
        }
        return this;
    }

    private void genFileAbsolutePath(ConfigBuilder configBuilder) throws Exception{

        GenFileCfg cfg = configBuilder.getGenFileCfg();
        String projectPath = configBuilder.getGlobalConfig().projectPath;
        Map<String,Object> templateData = configBuilder.getTemplateData();
        for (int i=0;i<cfg.getTmpRootDirs().size();i++) {
            File tmpRootDir = cfg.getTmpRootDirs().get(i);
            //process(projectPath,tmpRootDir,templateData,cfg);
            if (tmpRootDir == null) throw new IllegalStateException("template root dir is not null");
            //循环子目录
            List<File> subFiles = FileUtil.queryAllNoIgnoreFile(tmpRootDir);
            for (int j=0;j<subFiles.size();j++) {
                File subFile = subFiles.get(j);
                execute(projectPath,tmpRootDir,templateData,subFile,cfg);
            }
        }
    }
    private void execute(String projectPath, File tmpRootDir, Map<String, Object> tempData, File subFile, GenFileCfg cfg) throws Exception{
        String templateFile = FileUtil.getRelativePath(tmpRootDir,subFile);
        if (StrUtil.isNotBlank(cfg.getCustomePath()) && templateFile(templateFile,cfg)) return ;
        String outPutFilePath = handleOutPutFilePath(tempData,templateFile,cfg);
        if (outPutFilePath.startsWith("java")) {
            String path = projectPath+File.separator+ GenCodeCfg.getSrcPackage();
            String source = path;
            outPutFilePath = outPutFilePath.substring("java".length());
            outPutFilePath = source+outPutFilePath;
            // 生成文件
            generateFileOrWriterFile(templateFile,outPutFilePath,tempData,cfg);
        } else if (outPutFilePath.startsWith("webapp")) {
            String path = projectPath+File.separator+GenCodeCfg.getWebPackage();
            String source = path;
            outPutFilePath = outPutFilePath.substring("webapp".length());
            outPutFilePath = source+outPutFilePath;
            generateFileOrWriterFile(templateFile,outPutFilePath,tempData,cfg);
        }
    }

    private String handleOutPutFilePath(Map<String, Object> tempData, String templateFile, GenFileCfg cfg) throws Exception{

        String outputFilePath = templateFile;
        int testExpressionIndex = -1;
        if ((testExpressionIndex = templateFile.indexOf('@')) !=-1){
            outputFilePath = templateFile.substring(0,testExpressionIndex);
            String testExpressionKey = templateFile.substring(testExpressionIndex+1);
            Object expressionVal = tempData.get(testExpressionKey);
            if (expressionVal == null) {
                return null;
            }

            if (!"true".equals(String.valueOf(expressionVal))) {
                return null;
            }
        }
        Configuration conf = this.init(cfg.getTmpRootDirs(),GenFileCfgConstants.SRC_ENCODE).configuration;
        outputFilePath = generatorTmplateStr(outputFilePath,tempData,conf);
        String customerPath = cfg.getCustomePath();
        if (StrUtil.isNotBlank(customerPath)) {
            outputFilePath = outputFilePath.substring(customerPath.length()+1);
        }
        String extName = outputFilePath.substring(outputFilePath.lastIndexOf("."));
        String fileName = outputFilePath.substring(0,outputFilePath.lastIndexOf(".")).replace(".",File.separator);
        outputFilePath = fileName+extName;

        return outputFilePath;
    }

    private void generateFileOrWriterFile(String templateFile, String outPutFilePath, Map<String, Object> tempData, GenFileCfg cfg) throws Exception {

        if (outPutFilePath.endsWith("i")) {
            outPutFilePath = outPutFilePath.substring(0,outPutFilePath.length()-1);
        }
        Template template = getFreemarkerTemplate(templateFile,cfg);
        template.setOutputEncoding(GenFileCfgConstants.SRC_ENCODE);
        File absoluteOutputFilePath = GenFileUtil.createParentDir(outPutFilePath);
        generatorTemplate(template,tempData,absoluteOutputFilePath,GenFileCfgConstants.SRC_ENCODE);
        if (!GenFileUtil.isCutFile(absoluteOutputFilePath)) {
            genFileRes.add("生成成功:"+outPutFilePath);
        }

        if (GenFileUtil.isCutFile(absoluteOutputFilePath)) {
            //分割文件
            GenFileUtil.cutFile(absoluteOutputFilePath,"#segment#",genFileRes);
        }
    }

    // 生成模板
    private  void generatorTemplate(Template template,Map<String,Object> map,
                                    File outFilePath,String encod) throws IOException,TemplateException {

        Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFilePath),encod));
        map.put("Format",new FreemarkerFormatUtil());
        template.process(map,out);
        out.close();
    }

    /***
     * 根据map数据源填充模板内容
     * @param tempStr 模板
     * @param map 数据源
     * @param cfg freemarker配置对象
     * @return
     */
    private String generatorTmplateStr(String tempStr, Map<String,Object> map, Configuration cfg) {
        StringWriter writer = new StringWriter();
        try {
            Template template = new Template("tempStr",new StringReader(tempStr),cfg);
            template.process(map,writer);
            return writer.toString();
        }catch (Exception e) {
            throw new IllegalStateException("can not generator tempStr:"+tempStr+" case:"+e,e);
        }
    }

    private Template getFreemarkerTemplate(String templateFile, GenFileCfg cfg) throws IOException{
        Template template = this.init(cfg.getTmpRootDirs(), GenFileCfgConstants.SRC_ENCODE).configuration.getTemplate(templateFile);
        return template;

    }

    private boolean templateFile(String templateFile,GenFileCfg cfg) {
        return !templateFile.replace(File.separator,".").startsWith(cfg.getCustomePath());
    }
}
